﻿using AutoMapper;
using Blm.PgsqlDriver.Helpers;
using Blm.Utils.Extensions;
using Npgsql;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VisionCloud.Core.Model.Ms;
using VisionCloud.Domain.Common;
using VisionCloud.Domain.DtoModel.Ms;
using VisionCloud.Domain.Extension;
using VisionCloud.Domain.Repository.Interface.Ms;
using VisionCloud.Domain.UnitOfWork;
using VisionCloud.Utility;

namespace VisionCloud.Service.Ms
{
    public class AlgorithmParamTemplateService
    {
        /// <summary>
        /// 默认算法模板ID：00000000-0000-0000-0000-000000000000
        /// </summary>
        private readonly Guid defaultAlgorithmTemplateId = Guid.Empty;

        private static readonly string fileserver = ConfigHelper.GetValue<string>("FileServer");

        public IMsProjectRepository MsProjectRepository { get; set; }

        public IAlgorithmRepository AlgorithmRepository { get; set; }

        public IMapper Mapper { set; get; }

        public IUnitOfWork UnitOfWork { get; set; }

        public IMsProjectEditLogRepository MsProjectEditLogRepository { set; get; }

        /// <summary>
        /// 获取默认算法模板
        /// </summary>
        /// <returns></returns>
        public async Task<MsAlgorithmParamTemplate> GetDefaultAlgorithmTemplate()
        {
            var defaultAlgorithmTemplate = await AlgorithmRepository.GetByIdAsync(defaultAlgorithmTemplateId);
            return defaultAlgorithmTemplate;
        }

        /// <summary>
        /// 根据项目ID获取算法模板
        /// </summary>
        /// <param name="projectId"></param>
        /// <returns></returns>
        public async Task<List<MsAlgoTempDto>> GetAlgorithmTemplates(Guid projectId)
        {
            var defaultAlgorithmTemplate = await GetDefaultAlgorithmTemplate();
            var algorithmTemplates = new List<MsAlgorithmParamTemplate> { defaultAlgorithmTemplate };
            var projectAlgorithmTemplates = await AlgorithmRepository.GetListByProjectId(projectId);
            algorithmTemplates.AddRange(projectAlgorithmTemplates);
            return Mapper.Map<List<MsAlgoTempDto>>(algorithmTemplates);
        }

        /// <summary>
        /// 软删除算法模板
        /// </summary>
        /// <param name="algorithmTemplateId"></param>
        /// <param name="userId"></param>
        /// <returns></returns>
        public async Task DeleteAlgorithmTemplate(Guid algorithmTemplateId, Guid userId)
        {
            if (algorithmTemplateId != defaultAlgorithmTemplateId)
            {
                var projectId = AlgorithmRepository.DeleteAlgorithmTemplate(algorithmTemplateId);
                await LogOparationRecord(userId, projectId, "删除", algorithmTemplateId);
                await UnitOfWork.SaveChangesAsync();
            }
        }

        /// <summary>
        /// 批量删除算法模板
        /// </summary>
        /// <param name="algorithmTemplateIds"></param>
        /// <returns></returns>
        public async Task DeleteAlgorithmTemplate(List<Guid> algorithmTemplateIds)
        {
            if (!algorithmTemplateIds.Contains(defaultAlgorithmTemplateId))
            {
                algorithmTemplateIds.ForEach(x => AlgorithmRepository.RemoveById(x));
                await UnitOfWork.SaveChangesAsync();
            }
        }

        public async Task<List<MsAlgorithmParamTemplateItemDto>> GetAlgorithmParamTemplateDetail(Guid algorithmParamTemplateGuid)
        {
            var curDir = Directory.GetParent(AppContext.BaseDirectory);
            string baseDir = curDir.Parent.FullName;
            string frameworkDir = Path.Combine(baseDir, $"framework/TemplateImages");
            if (!Directory.Exists(frameworkDir))
            {
                Directory.CreateDirectory(frameworkDir);
            }

            List<MsAlgorithmParamTemplateItemDto> listResult = new List<MsAlgorithmParamTemplateItemDto>();
            var list = await AlgorithmRepository.GetAlgorithmParamTemplateItems(algorithmParamTemplateGuid);

            if (list?.Count > 0)
            {
                /*
                 * 1.先找出测量项
                 */

                //测量项分组
                var measureTypeList = list.Select(p => new MsAlgoTempItemDto { MeasureType = p.MeasureType, TemplateName = p.TemplateName }).Distinct(new MsAlgoTempItemDtoComparer()).ToList();

                foreach (var measureType in measureTypeList)
                {
                    MsAlgorithmParamTemplateItemDto msAlgorithmParamTemplateItemDto = new MsAlgorithmParamTemplateItemDto();
                    msAlgorithmParamTemplateItemDto.MeasureType = measureType.MeasureType;
                    msAlgorithmParamTemplateItemDto.TemplateName = measureType.TemplateName;
                    msAlgorithmParamTemplateItemDto.AlgorithmParamTemplateGuid = algorithmParamTemplateGuid;
                    msAlgorithmParamTemplateItemDto.AlgorithmGroups = new List<AlgorithmGroup>();

                    //测量项的配置项分组
                    var groupList = list.Where(p => p.MeasureType == measureType.MeasureType).Select(p1 => new MsItemGroupDto { ItemGroup = p1.ItemGroup }).Distinct(new MsItemGroupDtoComparer()).ToList();
                    foreach (var group in groupList)
                    {
                        AlgorithmGroup algorithmGroup = new AlgorithmGroup();
                        algorithmGroup.ItemGroup = group.ItemGroup;
                        string itemGroupImagePath = string.Empty;
                        var picList = list.Where(p => p.MeasureType == measureType.MeasureType && p.ItemGroup == group.ItemGroup).ToList();
                        itemGroupImagePath = picList[0].ItemGroupImagePath;
                        algorithmGroup.ItemGroupImagePath = itemGroupImagePath;
                        algorithmGroup.ItemGroupImageUrl = ToWebUrl(Path.Combine("item-group-image", itemGroupImagePath));
                        msAlgorithmParamTemplateItemDto.AlgorithmGroups.Add(algorithmGroup);

                        //配置项某个分组的明细
                        var items = list.Where(p => p.MeasureType == measureType.MeasureType && p.ItemGroup == group.ItemGroup).OrderBy(x => x.ItemGroup).ToList();
                        algorithmGroup.ParamSettings = new List<ParamSetting>();
                        foreach (var item in items)
                        {
                            ParamSetting paramSetting = new ParamSetting();
                            paramSetting.AlgorithmParamItemGuid = item.AlgorithmParamItemGuid;
                            paramSetting.ItemKey = item.ItemKey;
                            paramSetting.ItemName = item.ItemName;
                            paramSetting.ItemType = item.ItemType;
                            paramSetting.ItemValue = item.ItemValue;
                            if (paramSetting.ItemValue == "1")
                            {
                                paramSetting.ItemBool = true;
                            }
                            paramSetting.ItemValueRange = item.ItemValueRange;
                            algorithmGroup.ParamSettings.Add(paramSetting);
                        }
                    }

                    listResult.Add(msAlgorithmParamTemplateItemDto);
                }
            }

            return listResult.OrderBy(x => x.MeasureType).ToList();
        }

        /// <summary>
        /// 事务批处理保存算法参数模板
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        public async Task<bool> SaveAlgorithmParamTemplateDetail(List<MsAlgorithmParamTemplateItemDto> list, Guid userId)
        {
            if (list == null)
            {
                throw new OperationException("参数异常");
            }
            if (list.FirstOrDefault().TemplateName.Length >= 50)
            {
                throw new OperationException("算法模板名称长度不能超过50");
            }

            List<NpgsqlParameter> parameters = new List<NpgsqlParameter>();

            StringBuilder sb = new StringBuilder();

            #region 先删除旧的数据

            sb.AppendLine("delete from \"MsProjectAlgorithm\" where \"AlgorithmId\"=@AlgorithmParamTemplateGuid;");
            sb.AppendLine("delete from \"MsAlgorithmParamTemplateItem\" where \"AlgorithmParamTemplateGuid\"=@AlgorithmParamTemplateGuid;");
            sb.AppendLine("delete from \"MsAlgorithmParamTemplate\" where \"AlgorithmParamTemplateGuid\"=@AlgorithmParamTemplateGuid;");

            #endregion 先删除旧的数据

            #region 插入头

            sb.AppendLine("insert into \"MsAlgorithmParamTemplate\"(\"AlgorithmParamTemplateGuid\", \"TemplateName\", \"TemplateType\", \"CreatedBy\", \"CreatedDate\", \"LastUpdatedBy\", \"LastUpdatedDate\", \"IsDeleted\")VALUES(@AlgorithmParamTemplateGuid,@TemplateName,@TemplateType,@CreatedBy,@CreatedDate,@LastUpdatedBy,@LastUpdatedDate,@IsDeleted);");
            sb.AppendLine("insert into \"MsProjectAlgorithm\"(\"AlgorithmId\", \"ProjectId\", \"CreatedBy\", \"CreatedDate\", \"LastUpdatedBy\", \"LastUpdatedDate\", \"IsDeleted\")VALUES(@AlgorithmParamTemplateGuid,@ProjectId,@CreatedBy,@CreatedDate,@LastUpdatedBy,@LastUpdatedDate,@IsDeleted);");

            #region NpgsqlParameter

            Guid algorithmParamTemplateGuid = list[0].AlgorithmParamTemplateGuid;
            NpgsqlParameter npgsqlParameter = new NpgsqlParameter();
            npgsqlParameter = new NpgsqlParameter();
            npgsqlParameter.ParameterName = "AlgorithmParamTemplateGuid";
            npgsqlParameter.Value = algorithmParamTemplateGuid;
            parameters.Add(npgsqlParameter);

            string templateName = list[0].TemplateName;
            npgsqlParameter = new NpgsqlParameter();
            npgsqlParameter.ParameterName = "TemplateName";
            npgsqlParameter.Value = templateName;
            parameters.Add(npgsqlParameter);

            var projectId = list[0].ProjectId;
            npgsqlParameter = new NpgsqlParameter();
            npgsqlParameter.ParameterName = "ProjectId";
            npgsqlParameter.Value = projectId;
            parameters.Add(npgsqlParameter);
            string sql = @"SELECT count(0) FROM ""MsAlgorithmParamTemplate"" t WHERE t.""TemplateName"" = @TemplateName AND t.""IsDeleted"" = FALSE AND t.""AlgorithmParamTemplateGuid"" != @AlgorithmParamTemplateGuid"
                            + @" AND EXISTS(SELECT 1 FROM ""MsProjectAlgorithm"" p WHERE p.""ProjectId"" = @ProjectId AND p.""AlgorithmId"" = t.""AlgorithmParamTemplateGuid""); ";
            int count = PgSqlHelper.QueryScalar<int>(sql, parameters.ToArray());
            if (count > 0)
            {
                throw new OperationException($"模板名称'{templateName}'已经使用过");
            }

            npgsqlParameter = new NpgsqlParameter();
            npgsqlParameter.ParameterName = "TemplateType";
            npgsqlParameter.Value = "";
            parameters.Add(npgsqlParameter);

            npgsqlParameter = new NpgsqlParameter();
            npgsqlParameter.ParameterName = "CreatedBy";
            npgsqlParameter.Value = userId;
            parameters.Add(npgsqlParameter);

            npgsqlParameter = new NpgsqlParameter();
            npgsqlParameter.ParameterName = "CreatedDate";
            npgsqlParameter.Value = DateTime.Now;
            parameters.Add(npgsqlParameter);

            npgsqlParameter = new NpgsqlParameter();
            npgsqlParameter.ParameterName = "LastUpdatedBy";
            npgsqlParameter.Value = userId;
            parameters.Add(npgsqlParameter);

            npgsqlParameter = new NpgsqlParameter();
            npgsqlParameter.ParameterName = "LastUpdatedDate";
            npgsqlParameter.Value = DateTime.Now;
            parameters.Add(npgsqlParameter);

            npgsqlParameter = new NpgsqlParameter();
            npgsqlParameter.ParameterName = "IsDeleted";
            npgsqlParameter.Value = false;
            parameters.Add(npgsqlParameter);

            #endregion NpgsqlParameter

            #endregion 插入头

            foreach (var dto in list)
            {
                /*
                 * 1.dto为一个测量项
                 * 2.获取测量项的分组AlgorithmGroups
                 * 3.获取测量项的的分组的明细
                 */

                for (int i = 0; i < dto.AlgorithmGroups.Count; i++)
                {
                    var algoGroup = dto.AlgorithmGroups[i];
                    for (int j = 0; j < algoGroup.ParamSettings.Count; j++)
                    {
                        var setting = algoGroup.ParamSettings[j];
                        string pIndex = i.ToString() + j.ToString() + dto.MeasureType;
                        sb.AppendLine($"insert into \"MsAlgorithmParamTemplateItem\" (\"AlgorithmParamItemGuid\",\"AlgorithmParamTemplateGuid\",\"ItemName\",\"ItemKey\",\"ItemValue\",\"ItemType\",\"ItemGroup\",\"MeasureType\",\"CreatedBy\",\"CreatedDate\",\"LastUpdatedBy\",\"LastUpdatedDate\",\"ItemGroupImagePath\",\"ItemValueRange\",\"IsDeleted\")values(@AlgorithmParamItemGuid{pIndex},@AlgorithmParamTemplateGuid{pIndex},@ItemName{pIndex},@ItemKey{pIndex},@ItemValue{pIndex},@ItemType{pIndex},@ItemGroup{pIndex},@MeasureType{pIndex},@CreatedBy,@CreatedDate,@LastUpdatedBy,@LastUpdatedDate,@ItemGroupImagePath{pIndex},@ItemValueRange{pIndex},@IsDeleted);");

                        #region NpgsqlParameter

                        npgsqlParameter = new NpgsqlParameter();
                        npgsqlParameter.ParameterName = $"AlgorithmParamItemGuid{pIndex}";
                        npgsqlParameter.Value = Guid.NewGuid();
                        parameters.Add(npgsqlParameter);

                        npgsqlParameter = new NpgsqlParameter();
                        npgsqlParameter.ParameterName = $"AlgorithmParamTemplateGuid{pIndex}";
                        npgsqlParameter.Value = algorithmParamTemplateGuid;
                        parameters.Add(npgsqlParameter);

                        npgsqlParameter = new NpgsqlParameter();
                        npgsqlParameter.ParameterName = $"ItemName{pIndex}";
                        npgsqlParameter.Value = setting.ItemName;
                        parameters.Add(npgsqlParameter);

                        npgsqlParameter = new NpgsqlParameter();
                        npgsqlParameter.ParameterName = $"ItemKey{pIndex}";
                        npgsqlParameter.Value = setting.ItemKey;
                        parameters.Add(npgsqlParameter);

                        npgsqlParameter = new NpgsqlParameter();
                        npgsqlParameter.ParameterName = $"ItemValue{pIndex}";
                        npgsqlParameter.Value = setting.ItemValue;
                        parameters.Add(npgsqlParameter);

                        npgsqlParameter = new NpgsqlParameter();
                        npgsqlParameter.ParameterName = $"ItemType{pIndex}";
                        npgsqlParameter.Value = setting.ItemType;
                        parameters.Add(npgsqlParameter);

                        npgsqlParameter = new NpgsqlParameter();
                        npgsqlParameter.ParameterName = $"ItemGroup{pIndex}";
                        npgsqlParameter.Value = algoGroup.ItemGroup;
                        parameters.Add(npgsqlParameter);

                        npgsqlParameter = new NpgsqlParameter();
                        npgsqlParameter.ParameterName = $"MeasureType{pIndex}";
                        npgsqlParameter.Value = dto.MeasureType;
                        parameters.Add(npgsqlParameter);

                        npgsqlParameter = new NpgsqlParameter();
                        npgsqlParameter.ParameterName = $"ItemGroupImagePath{pIndex}";
                        npgsqlParameter.Value = algoGroup.ItemGroupImagePath;
                        parameters.Add(npgsqlParameter);

                        npgsqlParameter = new NpgsqlParameter();
                        npgsqlParameter.ParameterName = $"ItemValueRange{pIndex}";
                        npgsqlParameter.Value = setting.ItemValueRange;
                        parameters.Add(npgsqlParameter);

                        #endregion NpgsqlParameter
                    }
                }
            }

            int affrows = 0;
            PgSqlHelper.Transaction(() =>
            {
                affrows = PgSqlHelper.ExecuteNonQuery(CommandType.Text, sb.ToString(), parameters.ToArray());
            });
            if (affrows <= 0)
                return false;

            await LogOparationRecord(userId, list[0].ProjectId.Value, "新增或修改", algorithmParamTemplateGuid, templateName);
            await UnitOfWork.SaveChangesAsync();
            return true;
        }

        /// <summary>
        /// 记录项目的算法规则操作记录
        /// </summary>
        /// <param name="userId">操作人员ID</param>
        /// <param name="projectId">项目ID</param>
        /// <param name="logContent">日志内容</param>
        /// <param name="algorithmTemplateId">算法规则ID</param>
        /// <param name="algorithmTemplateName">算法规则名称</param>
        /// <returns></returns>
        public async Task LogOparationRecord(Guid userId, Guid projectId, string logContent, Guid algorithmTemplateId, string algorithmTemplateName = null)
        {
            var projectEntity = await MsProjectRepository.FirstOrDefaultAsync(x => x.ProjectId == projectId);
            var projectName = projectEntity.Name;
            if (algorithmTemplateName == null)
            {
                algorithmTemplateName = (await AlgorithmRepository.GetByIdAsync(algorithmTemplateId)).TemplateName;
            }
            logContent = $"{logContent}【{algorithmTemplateName}】算法规则模板";
            MsProjectRepository.UpdateProjectVersion(projectId);
            MsProjectEditLogRepository.Add(new MsProjectEditLog
            {
                EditLogId = Guid.NewGuid(),
                CreatedBy = userId,
                CreatedDate = DateTime.Now,
                EditType = MsProjectEditTypeEnum.算法规则.ToString(),
                ProjectId = projectId,
                ProjectName = projectName,
                IsDeleted = false,
                IsPublished = true,
                ChangeLog = logContent
            });
        }

        private string ToWebUrl(string path)
        {
            return $"{fileserver}/algorithmtemplateImage/{path}";
        }

        #region Comparer

        private class MsAlgoTempItemDtoComparer : IEqualityComparer<MsAlgoTempItemDto>
        {
            public bool Equals(MsAlgoTempItemDto x, MsAlgoTempItemDto y)
            {
                if (x == null)
                {
                    return false;
                }
                if (y == null)
                {
                    return false;
                }
                if (x.MeasureType == y.MeasureType && x.TemplateName == y.TemplateName)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }

            public int GetHashCode(MsAlgoTempItemDto obj)
            {
                if (obj == null)
                    return 0;
                return (obj.MeasureType + obj.TemplateName).GetHashCode();
            }
        }

        private class MsItemGroupDtoComparer : IEqualityComparer<MsItemGroupDto>
        {
            public bool Equals(MsItemGroupDto x, MsItemGroupDto y)
            {
                if (x == null)
                {
                    return false;
                }
                if (y == null)
                {
                    return false;
                }
                //if (x.ItemGroup == y.ItemGroup && x.ItemGroupImagePath == y.ItemGroupImagePath)
                if (x.ItemGroup == y.ItemGroup)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }

            public int GetHashCode(MsItemGroupDto obj)
            {
                if (obj == null)
                    return 0;
                return (obj.ItemGroup).GetHashCode();
            }
        }

        #endregion Comparer
    }
}